<?php
// @formatter:off
/**
 * @file WCRuleAction.php
 * @author Alejandro Dario Simi
 * @date $Date: 2013-08-20 00:23:06 +0000 (Tue, 20 Aug 2013) $
 *
 * $Id: WCRuleAction.php 102 2013-08-20 00:23:06Z daemonraco@gmail.com $
 * $URL: http://wcomix.googlecode.com/svn/tags/wcomix-1.0.0.1/includes/WCRuleAction.php $
 */
// @formatter:on

require_once "{$wcPaths["includes-directory"]}/search/WCSearch.php";

/**
 * @abstract
 * @class WCRuleAction This is a general representation of a rule's action. It
 * holds all the basic logic to manage and store an action on database.
 */
abstract class WCRuleAction implements WCSearchIndexable, WCPreviews {
	/**
	 * @var boolean This flag indicates if current action is in conflict with
	 * others.
	 */
	protected $_conflict = false;
	/**
	 * @var WCGroup Action's group shortcut.
	 */
	protected $_group;
	/**
	 * @var int Action's parent rule id.
	 */
	protected $_ruleId;
	/**
	 * @var int Action's parent rule shortcut.
	 */
	protected $_rule = false;
	/**
	 * @var int Current action id on database.
	 */
	protected $_id;
	/**
	 * @var mixed[string] This is the information retrieved from column
	 * 'rel_ra_info' and through it represent an action extra information. It
	 * is used by pseudo-method __get() to avoid checking database on each
	 * access and also by __set() to perform its formulas before updating
	 * database.
	 */
	protected $_info = array();
	/**
	 * @var boolean This flag indicates if this action is indexed or not by
	 * the search engine.
	 */
	protected $_indexed;
	/**
	 * @var string This is current's name.
	 */
	protected $_name;
	/**
	 * @var boolean This flag indicates if current action is broken or not.
	 */
	protected $_ok = false;
	/**
	 * @var string This is name of this action's type and the indicator of its
	 * behavior.
	 */
	protected $_type = false;
	/**
	 * @var boolean This flag indicates that even when this action is in
	 * conflict with others it must be run.
	 */
	protected $_ignoreConfics = false;
	/**
	 * @var WCConnection Database pointer shortcut.
	 */
	protected $_db;
	/**
	 * @var string Database tables' prefix shortcut.
	 */
	protected $_dbprefix;
	/**
	 * Class constructor.
	 */
	public function __construct($ruleId, $actionId, WCGroup &$group) {
		//
		// Loading global pointers.
		global $wcDatabaseAccess;
		//
		// Loading shortcuts.
		// @{
		$this->_db = WCConnection::Instance();
		$this->_dbprefix = $wcDatabaseAccess["prefix"];
		$this->_group = $group;
		$this->_ruleId = $ruleId;
		// @}
		//
		// Setting current action's name and ID.
		// @{
		$this->_id = $actionId;
		$this->_name = $actionId;
		// @}
		//
		// Calling basic loader.
		$this->mainLoad();
		//
		// Calling sub-class loader.
		$this->load();
	}
	//
	// Public methods.
	/**
	 * This pseudo-method gives an easy way to access current action's
	 * data using column names (without prefixes).
	 *
	 * @param string $name Name of a column (without prefixes).
	 * @return mixed Returns column's value.
	 */
	public function __get($name) {
		return isset($this->_info[$name]) ? $this->_info[$name] : null;
	}
	/**
	 * This pseudo-method gives an easy way to modify database columns for a
	 * specific row represented by this object.
	 *
	 * @param string $name Property name (without prefix).
	 * @param string $value Value to be set.
	 * @return boolean Returns true when the value was successfully set in the
	 * database.
	 */
	public function __set($name, $value) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Setting default value to be returned.
		$out = false;
		//
		// Checking that Read Only Mode is not activated and the action is
		// okey.
		if(!$wcDefaults["read-only"] && $this->ok()) {
			//
			// Updating memory cache.
			$this->_info[$name] = $value;
			//
			// Creating a statement to update dynamic
			// properties.
			// @{
			$stmt = false;
			$stmtID = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtID)) {
				$stmt = $this->_db->get($stmtID);
			} else {
				$query = "update  {$this->_dbprefix}rule_actions\n";
				$query .= "set     rel_ra_info = :info\n";
				$query .= "where   rel_ra_rule   = :rule\n";
				$query .= " and    rel_ra_type   = :type\n";
				$query .= " and    rel_ra_action = :action\n";

				$stmt = $this->_db->prepare($stmtID, $query);
			}
			// @}
			//
			// Building database parameters.
			$params = array(
				":info" => serialize($this->_info),
				":rule" => $this->_ruleId,
				":type" => $this->_type,
				":action" => $this->_id
			);
			//
			// Updating dynamic properties.
			$stmt->execute($params);
			//
			// Checking if data was modified or not.
			$out = $stmt->rowCount() > 0;
		}
		//
		// Returning addition status.
		return $out;
	}
	/**
	 * This method allows to know if current action is in conflict with
	 * others.
	 *
	 * @return boolean Returns true when it's in conflict.
	 */
	public function conflict() {
		return $this->_conflict;
	}
	/**
	 * This method allows to regenerate current action's previews.
	 *
	 * @return boolean Returns true when previews where successfully
	 * regenerated.
	 */
	public function generatePreviews() {
		return false;
	}
	/**
	 * This method allows to access current action's group.
	 *
	 * @return WCGroup Returns a group pointer.
	 */
	public function group() {
		return $this->_group;
	}
	/**
	 * This method allows to know if this action has a preview image.
	 *
	 * @return boolean Returns true when this action has at least one preview.
	 */
	public function hasPreview() {
		return false;
	}
	/**
	 * This method allows to know if this action has a list of preview images.
	 *
	 * @return boolean Returns true when this action has at least one preview.
	 */
	public function hasPreviews() {
		return false;
	}
	/**
	 * This method allows to know current action's ID.
	 *
	 * @return int Returns an action ID.
	 */
	public function id() {
		return $this->_id;
	}
	/**
	 * This method allows to know if current action is ignoring conflicts.
	 *
	 * @return boolean Returns a ignore-conflicts status.
	 */
	public function ignoreConfics() {
		return $this->_ignoreConfics;
	}
	/**
	 * This method allows to get and also set the search engine indexed status
	 * from the current action.
	 *
	 * @param boolean $value When this option is not NULL, it indicates the
	 * status to set.
	 * @return boolean Returns the search engine indexed status.
	 */
	public function indexed($value = null) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Checking that:
		//	- Read Only Mode is not activated.
		//	- there's a status to be set.
		//	- the status to set is different that the current one.
		if(!$wcDefaults["read-only"] && $value !== null && $value != $this->_indexed) {
			//
			// Creating a statement to set the new status.
			// @{
			$stmt = false;
			$stmtID = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtID)) {
				$stmt = $this->_db->get($stmtID);
			} else {
				$query = "update  {$this->_dbprefix}rule_actions\n";
				$query .= "set     rel_ra_indexed = :indexed\n";
				$query .= "where   rel_ra_rule    = :rule\n";
				$query .= " and    rel_ra_type    = :type\n";
				$query .= " and    rel_ra_action  = :action\n";

				$stmt = $this->_db->prepare($stmtID, $query);
			}
			// @}
			//
			// Building database parameters.
			$params = array(
				":rule" => $this->_ruleId,
				":type" => $this->_type,
				":action" => $this->_id,
				":indexed" => $value ? "Y" : "N"
			);
			//
			// Attempting to set the new status.
			if($stmt->execute($params) && $stmt->rowCount() > 0) {
				//
				// At this point, status was successfully updated.
				$this->_indexed = $value;
			}
		}
		//
		// Returning search engine indexed status.
		return $this->_indexed;
	}
	public function info() {
		return $this->_info;
	}
	/**
	 * This method allows to know current action's name.
	 *
	 * @return string Returns an action name.
	 */
	public function name() {
		return $this->_name;
	}
	/**
	 * This method allows to know if current action has issues.
	 *
	 * @return boolean Returns true when current action has issues.
	 */
	public function ok() {
		return $this->_ok;
	}
	/**
	 * This method allows to get the thumbnail associated with current action.
	 *
	 * @return string Returns an image URIs.
	 */
	public function preview() {
		return "";
	}
	/**
	 * This method allows to get the list of thumbnails associated with
	 * current action.
	 *
	 * @return string[] Returns a list of image URIs.
	 */
	public function previews() {
		return array();
	}
	/**
	 * This method allows to get the rule where this action is attached to.
	 *
	 * @return WCRule Returns a rule pointer.
	 */
	public function rule() {
		//
		// Checking if rule pointer is already cached.
		if(!$this->_rule) {
			//
			// Loading global pointers.
			global $wcRulesHolder;
			//
			// Loading rule
			$this->_rule = @$wcRulesHolder->item($this->_ruleId, $this->_group);
			//
			// Checking that the rule exists and it has no issues.
			if(!$this->_rule || !$this->_rule->ok()) {
				$this->_rule = false;
			}
		}
		//
		// Returning current action's rule.
		return $this->_rule;
	}
	/**
	 * @abstract
	 * This is the method to call when current action must be run.
	 *
	 * @return mixed[] Returns an inform useful for cron tool
	 * 'ct.rules.run.php'.
	 */
	public function run() {
		die(__CLASS__."::".__FUNCTION__."() Subclass Resposabillity\n");
		return array();
	}
	/**
	 * This method reload all object data.
	 */
	public function reload() {
		//
		// Resetting values.
		// @{
		$this->_conflict = false;
		$this->_name = $this->_id;
		$this->_ok = false;
		$this->_type = false;
		$this->_rule = false;
		// @}
		//
		// Basic data loading.
		$this->mainLoad();
		//
		// Specific data loading.
		$this->load();
	}
	/**
	 * This method allows to remove a rule action from the database and
	 * triggers the removal of other related information.
	 */
	public function remove() {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Setting default value to be returned.
		$out = false;
		//
		// Checking that Read Only Mode is not activated.
		if(!$wcDefaults["read-only"]) {
			//
			// Checking if the action has a valid type.
			if($this->type()) {
				$this->rule()->indexed(false);
				//
				// Creating a statement to delete a rule action.
				// @{
				$stmt = null;
				$stmtID = __CLASS__."::".__FUNCTION__;
				if($this->_db->has($stmtID)) {
					$stmt = $this->_db->get($stmtID);
				} else {
					$query = "delete from {$this->_dbprefix}rule_actions\n";
					$query .= "where       rel_ra_action = :id";
					$stmt = $this->_db->prepare(__CLASS__."::".__FUNCTION__, $query);
				}
				// @}
				//
				// Removing action.
				$stmt->execute(array(":id" => $this->id()));
				//
				// Triggering related removals.
				$out = $this->removeSpecific();
			} else {
				//
				// If there's not a valid type, it is a wrongly
				// set action.
				trigger_error(__CLASS__."::".__FUNCTION__."() Not type set", E_USER_ERROR);
				$out = false;
			}
		}
		//
		// Returning removal status.
		return $out;
	}
	public function setIgnoreConfics($ignore) {
		$this->_ignoreConfics = $ignore;
		return $this->ignoreConfics();
	}
	/**
	 * This method allows to set the conflict status for the current action.
	 *
	 * @param boolean $value Status to set.
	 * @return boolean Returns the status.
	 */
	public function setConflict($status) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Checking that Read Only Mode is not activated and the status to
		// set is different than the current one.
		if(!$wcDefaults["read-only"] && $status != $this->_conflict) {
			//
			// Creating a statement to update conflict status.
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "update  {$this->_dbprefix}rule_actions\n";
				$query .= "set     rel_ra_conflict = :conflict\n";
				$query .= "where   rel_ra_rule     = :rule\n";
				$query .= " and    rel_ra_action   = :action\n";
				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}
			//
			// Building database parameters.
			$params = array(
				":rule" => $this->_ruleId,
				":action" => $this->_id,
				":conflict" => $status ? "Y" : "N"
			);
			//
			// Updating status.
			$stmt->execute($params);
			//
			// If this action falls in conflict, its rule do it as
			// well.
			if($status) {
				$this->rule()->conflict = true;
			}
		}
		//
		// Returning conflict status.
		return $this->conflict();
	}
	/**
	 * This method allows to change action's name.
	 *
	 * @param string $name Name to be set.
	 * @return string Returns action name.
	 */
	public function setName($name) {
		//
		// Loading global pointers.
		global $wcDefaults;
		//
		// Checking that Read Only Mode is not activated.
		if(!$wcDefaults["read-only"]) {
			//
			// Creating a statement to alter action's name.
			// @{
			$stmt = null;
			$stmtId = __CLASS__."::".__FUNCTION__;
			if($this->_db->has($stmtId)) {
				$stmt = $this->_db->get($stmtId);
			} else {
				$query = "update  {$this->_dbprefix}rule_actions\n";
				$query .= "set     rel_ra_name   = :newname\n";
				$query .= "where   rel_ra_rule   = :rule\n";
				$query .= " and    rel_ra_action = :action\n";
				$stmt = $this->_db->prepare($stmtId, $query);
			}
			// @}
			//
			// Changing name.
			// @{
			$params = array(
				":rule" => $this->_ruleId,
				":action" => $this->_id,
				":newname" => $name
			);
			if($stmt->execute($params) && $stmt->rowCount() > 0) {
				//
				// At this point, name was changed therefore the
				// object is reloaded.
				$this->reload();
			}
			// @}
		}
		//
		// Returning final name.
		return $this->name();
	}
	/**
	 * This method allows to know current action's type.
	 *
	 * @return string Returns an action's type.
	 */
	public function type() {
		return $this->_type;
	}
	//
	// Protected methods.
	/**
	 * @abstract
	 * This method is used to load all the action data that hasn't been loaded
	 * by 'mainLoad()'.
	 */
	protected function load() {
		die(__CLASS__."::".__FUNCTION__."() Subclass Resposabillity\n");
	}
	/**
	 * This method is the basic loader and it goes beyond all sub-class. The
	 * information loaded is the one on table '%rule_actions'.
	 */
	final protected function mainLoad() {
		//
		// Creating a method to load actions general data.
		// @{
		$stmt = null;
		$stmtId = __CLASS__."::".__FUNCTION__;
		if($this->_db->has($stmtId)) {
			$stmt = $this->_db->get($stmtId);
		} else {
			$query = "select  *\n";
			$query .= "from    {$this->_dbprefix}rule_actions\n";
			$query .= "where   rel_ra_rule   = :rule\n";
			$query .= " and    rel_ra_action = :action\n";
			$stmt = $this->_db->prepare($stmtId, $query);
		}
		// @}
		//
		// Building database parameters.
		$params = array(
			":rule" => $this->_ruleId,
			":action" => $this->_id
		);
		//
		// Asking information.
		if($stmt->execute($params) && $stmt->rowCount() > 0) {
			//
			// Fetching information.
			$row = $stmt->fetch();
			//
			// Loading action's type.
			$this->_type = $row["rel_ra_type"];
			//
			// Loading action's name.
			$this->_name = $row["rel_ra_name"];
			//
			// Loading indexation flag.
			$this->_indexed = $row["rel_ra_indexed"] == "Y";
			//
			// Loading conflict flag.
			$this->_conflict = $row["rel_ra_conflict"] == "Y";
			//
			// Loading action's extra information.
			$this->_info = unserialize($row["rel_ra_info"]);
		}
	}
	/**
	 * @abstract
	 * This method is called when action is being removed. It allows sub-
	 * classes to remove specific data that wcomix's core doesn't know.
	 *
	 * @return boolean Returns true when this operation was performed
	 * successfully.
	 */
	protected function removeSpecific() {
		die(__CLASS__."::".__FUNCTION__."() Subclass Resposabillity\n");
		return false;
	}
	//
	// Public class methods.
	/**
	 * This is a sorting method based on action names and it can be used on
	 * usort and uasort functions.
	 *
	 * @param WCRuleAction $a Left operand.
	 * @param WCRuleAction $b Right operand.
	 * @return boolean Returns true when the id of $a is greater than
	 * $b's.
	 */
	public static function IdSort(WCRuleAction &$a, WCRuleAction &$b) {
		return $a->id() > $b->id();
	}
	/**
	 * This is a sorting method based on action names and it can be used on
	 * usort and uasort functions.
	 *
	 * @param WCRuleAction $a Left operand.
	 * @param WCRuleAction $b Right operand.
	 * @return boolean Returns true when the name of $a is greater than
	 * $b's.
	 */
	public static function NameSort(WCRuleAction &$a, WCRuleAction &$b) {
		return $a->name() > $b->name();
	}
	/**
	 * This is the general sorting method and it can be used on usort and
	 * uasort functions.
	 *
	 * @param WCRuleAction $a Left operand.
	 * @param WCRuleAction $b Right operand.
	 * @return boolean Returns true when $a goes after $b.
	 */
	public static function Sort(WCRuleAction &$a, WCRuleAction &$b) {
		return self::NameSort($a, $b);
	}
}
?>